#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##--------------------------------------------------------------------------##
__docformat__ = "restructuredtext en"
__all__ = []

from .. import formlib as layout
from .. import hint
from .. import pilelib as stack
from ..gamelib import info as gi, state, game
from ..forms import (gypsy as GypsyLayout, freecell as FreeCellLayout,)
from ..packlib import info as cs, place
from ..piles import foundation as FoundationStack
from ..utillib.misc import kwdefault

Wrap = stack.StackWrapper
GT, SL = gi.GT, gi.SL


class Bisley(game.Game):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.gypsy.GypsyLayout
        - row [12 over 2 W foundation, vert offset]
        - talon [1 SE, no offset]
        - foundation [8 over 2 row E, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.foundation.AceKingSS_Foundation, max_move=0)
        Init ace per W. Build in rank by same suit. W up; E down. Play none.
        Base ace E, king W.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.UD_SS_RowStack (max_move=1, base_rank=GAP)
        Init 4 open card per. Build either in rank by same suit. Play top.
        Base none.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.DefaultHint

    seealso::
      http://en.wikipedia.org/wiki/Bisley_%28solitaire%29
    """
    __info__ = dict(id=0x122, name='Bisley', game_type=(GT.ONE_DECK |
                    GT.OPEN), decks=1, skill_level=SL.MOSTLY_SKILL,
                    version='0.80fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = GypsyLayout.GypsyLayout
    _talon_ = stack.InitialDealTalon
    _foundation_ = Wrap(FoundationStack.AceKingSS_Foundation, max_move=0)
    _row_ = Wrap(stack.UD_SS_RowStack, base_rank=cs.GAP)

    def _create_game(self, rows=12, ncols=6, playcards=8, **kw):
        """
        _create_game([rows=12, ncols=6, playcards=8, bases=SUITS * 2]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.gypsy` for keyword definitions.
        """
        l = layout.Layout(self)
        kw.setdefault('bases', l.SUITS * 2)
        return super(Bisley, self)._create_game(rows=rows, ncols=ncols,
                                                playcards=playcards, **kw)

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on top by suit, deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllAce)

    def _start(self, rows=4, flip=True, **kw):
        """
        Deal initial cards for new game.
          - s[foundations] base.
          - s[rows] 4 ea up.

        .. \_start([rows=4, flip=True]) -> None
        """
        self.s['talon'].dealBaseCards(ncards=self['decks'] * len(self['suits']),
                                      saveinfo=False)
        for i in range(rows - 1):
            self.s['talon'].dealRow(flip=flip, frames=0)
        self.s['talon'].dealRow()


class DoubleBisley(Bisley):
    """
    Finish 2 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.gypsy.GypsyLayout
        - row [24 over 3 W foundation, vert offset]
        - talon [1 SE, no offset]
        - foundation [24 over 4 row E, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.foundation.AceKingSS_Foundation, max_move=0)
        Init ace per W. Build in rank by same suit. W up; E down. Play none.
        Base ace E, king W.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.UD_SS_RowStack (max_move=1, base_rank=GAP)
        Init 4 open card per. Build either in rank by same suit. Play top.
        Base none.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.DefaultHint
    """
    __info__ = dict(id=0x174, name='Double Bisley', game_type=(GT.TWO_DECK |
                    GT.OPEN | GT.ORIGINAL), decks=2,
                    skill_level=SL.MOSTLY_SKILL, version='0.90fc',
                    altnames=('Bisley (2 Decks)',))
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundations_ = Wrap(FoundationStack.AceKingSS_Foundation, max_move=0)
    _hint_ = hint.CautiousDefaultHint

    def _create_game(self, **kw):
        """
        _create_game([rows=24, ncols=8]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.gypsy` for keyword definitions.
        """
        kwdefault(kw, rows=24, ncols=8)
        return super(DoubleBisley, self)._create_game(**kw)


class Gloria(game.Game):
    """
    Finish 2 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.GloriaLayout
        - row [24 over 2 S foundation, vert offset]
        - talon [1 SE, no offset]
        - foundation [8 horz N, no offset]
        - resereve [1 NW, 1 NE, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.foundation.AceKingSS_Foundation
        Init king per W. Build in rank by same suit. W up; E down. Play top.
        Base ace E, king W.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.BasicRowStack
        Init 4 open card per. Build none. Play top.

      _reserve_ : pysollib.stack.Stack
        .. pysollib.stacks.ReserveStack
        Build any top. Play top. Max 1 card.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.DefaultHint
    """
    __info__ = dict(id=0x175, name='Gloria', game_type=(GT.TWO_DECK | GT.OPEN
                    | GT.ORIGINAL), decks=2, version='0.90fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = layout.GloriaLayout
    _talon_ = stack.InitialDealTalon
    _foundation_ = FoundationStack.AceKingSS_Foundation
    _row_ = stack.BasicRowStack
    _reserve_ = stack.ReserveStack

    def _create_game(self, **kw):
        """
        _create_game([rows=24, ncols=12, playcards=5, reserves=2]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.freecell` for keyword definitions.
        """
        kwdefault(kw, rows=24, ncols=12, playcards=5, reserves=2)
        return super(Gloria, self)._create_game(**kw)

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE and KING on top by rank, suit for _foundation_
        deal.
        """
        return place.MoveToTop(cards, place.AllKing)

    def _start(self, rows=4, flip=True, **kw):
        """
        Deal initial cards for new game.
          - s[foundations] base.
          - s[rows] 4 ea up.

        .. \_start([rows=4, flip=True]) -> None
        """
        self.s['talon'].dealBaseCards(ncards=len(self.s['foundations']),
                                      saveinfo=False)
        for i in range(rows - 1):
            talon.dealRow(flip=flip, frames=0)
        talon.dealRow()


class Realm(game.Game):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.GloriaLayout
        - row [8 horz S foundation, vert offset]
        - talon [1 SE, no offset]
        - foundation [8 horz N, no offset]
        - resereve [1 NW, 1 NE, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.foundation.AceKingSS_Foundation (max_move=0)
        Init king per W. Build in rank by same suit. W up; E down. Play none.
        Base ace E, king W.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.UD_AC_RowStack (base_rank=GAP)
        Init all open, min 6 card. Build either in rank by alt color. Play
        top. Base none.

      _reserve_ : pysollib.stack.Stack
        .. pysollib.stacks.ReserveStack
        Build any top. Play top. Max 1 card.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0x176, name='Realm', game_type=(GT.ONE_DECK | GT.OPEN |
                    GT.ORIGINAL), decks=1, version='0.90fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = FreeCellLayout.GloriaLayout
    _talon_ = stack.InitialDealTalon
    _foundations_ = Wrap(FoundationStack.AceKingSS_Foundation, max_move=0)
    _row_ = Wrap(stack.UD_AC_RowStack, base_rank=cs.GAP)
    _hint_ = hint.CautiousDefaultHint

    def _create_game(self, rows=8, playcards=15, **kw):
        """
        _create_game([rows=8, playcards=15, bases=l.SUITS * 2]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.fortythieves` for keyword definitions.
        """
        l = layout.Layout(self)
        kw.setdefault('bases', l.SUITS * 2)
        return super(Realm, self)._create_game(rows=rows, playcards=playcards,
                                               **kw)

    def _start(self, rows=7, flip=True, **kw):
        """
        Deal initial cards for new game.
          - s[rows] ~7 ea up.

        .. \_start([rows=7, flip=True]) -> None
        """
        for i in range(rows - 1):
            self.s['talon'].dealRow(flip=flip, frames=0)
        self.s['talon'].dealRow()
        self.s['talon'].dealRowAvail()


class Mancunian(Realm):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.GloriaLayout
        - row [8 horz S foundation, vert offset]
        - talon [1 SE, no offset]
        - foundation [8 horz N, no offset]
        - resereve [1 NW, 1 NE, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.foundation.AceKingSS_Foundation (max_move=0)
        Init king per W. Build in rank by same suit. W up; E down. Play none.
        Base ace E, king W.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.UD_RK_RowStack (base_rank=GAP)
        Init all open, min 6 card. Build either in rank alone. Play top. Base
        none.

      _reserve_ : pysollib.stack.Stack
        .. pysollib.stacks.ReserveStack
        Build any top. Play top. Max 1 card.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0x177, name='Mancunian', game_type=(GT.ONE_DECK |
                    GT.OPEN | GT.ORIGINAL), decks=1, version='0.90fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = Wrap(stack.UD_RK_RowStack, base_rank=cs.GAP)


class BoardPatience(Realm):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.freecell.GloriaLayout
        - row [10 horz S foundation, vert offset]
        - talon [1 SE, no offset]
        - foundation [8 horz N, no offset]
        - resereve [1 NW, 1 NE, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack.VarySS_Foundation
        Init base on 1. Build up in rank by same suit. Play top.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.UD_AC_RowStack  (mod=13)
        Init all open, min 5 card. Build either in rank by alt color wrap.
        Play top.

      _reserve_ : pysollib.stack.Stack
        .. pysollib.stacks.ReserveStack
        Build any top. Play top. Max 1 card.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0x2b4, name='Board Patience', game_type=(GT.ONE_DECK |
                    GT.OPEN), decks=1, skill_level=SL.MOSTLY_SKILL,
                    version='1.00fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundation_ = stack.Vary_SS_Foundation
    _row_ = Wrap(stack.UD_AC_RowStack, mod=13)

    def _create_game(self, **kw):
        """
        _create_game([rows=10, playcards=11]) -> Layout

        Add a canvas info label for base_rank next to last foundation.

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.fortythieves` for keyword definitions.
        """
        kwdefault(kw, rows=10, playcards=12)
        l, s = super(Realm, self)._create_game(**kw), self.s
        self.texts['info'] = l.create_info_text(s['foundations'][-1], anchor='ne')
        return l

    def _start(self, rows=6, **kw):
        """
        Deal initial cards for new game.
          - s[foundations] base. save all caps.
          - s[rows] ~6 ea up.

        .. \_start([rows=6]) -> None

        Deal base on 1 foundation. Deal [6, 5, 5, 5, 5, 5, 5, 5, 5, 5] open
        card on row. Update base rank for foundation.
        """
        base_rank = self.s['talon'].TOPCARD['rank']
        self.s['talon'].dealBaseCard(ncards=1, saveinfo=False)
        self._save_stack_caps(self.s['foundations'], base_rank=base_rank)
        for i in range(rows - 2):
            self.s['talon'].dealRow(frames=0)
        self.s['talon'].dealRow()
        self.s['talon'].dealRowAvail()

    @state.not_state('is_preview')
    def _update_text(self):
        """
        _update_text() -> None

        Update canvas info label with base_rank of foundation.
        """
        if not self.texts['info']:
            return
        base_rank = self.s['foundations'][0].cap['base_rank']
        text = '' if base_rank == cs.ANY else self.RANKS[base_rank]
        self.texts['info'].config(text=text)


class HospitalPatience(game.Game):
    """
    Finish 1 deck on foundation.

    Layout
      - talon [1 W, no offset]
      - waste [1 E talon, no offset]
      - foundation [8 over 2 E, no offset]

    :CVariables:
      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.WasteTalon (INFINITE, num_deal=3)
        Deal 3 card on waste on click. Inifinite redeal.

      _waste_ : pysollib.stack.Stack
        .. pysollib.stack.WasteStack
        Init 3 card per. Build none. Play top.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.foundation.AceKingSS_Foundation (max_move=0)
        Build in rank by same suit. N up; S down. Play none. Base ace N, king
        S.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0x2ae, name='Hospital Patience', game_type=GT.ONE_DECK,
                    decks=1, redeals=cs.INFINITE, skill_level=SL.MOSTLY_LUCK,
                    version='1.00fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.WasteTalon, max_rounds=cs.INFINITE, num_deal=3)
    _waste_ = stack.WasteStack
    _foundations_ = Wrap(FoundationStack.AceKingSS_Foundation, max_move=0)

    def _create_game(self, **kw):
        """
        _create_game([bases=l.SUITS * 2]) -> Layout

        :returns: current layout with piles and their texts set.

        :todo: needs seperate layout
        """
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + (2 + len(l.NSUITS * 2)) * l.XS, l.YM + 2 * l.YS)
        x, y = l.XM, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'ne')
        s['wastes'].append(self._waste_(x, y + l.YS, game=self))
        l.set_ncards_text(s['wastes'][-1], 'ne')
        x += 2 * l.XS
        y = l.YM
        for (x, y), suit in zip(self.col_block_iter(x, y, npiles=l.NSUITS * 2,
                                                ncols=l.NSUITS), l.SUITS * 2):
            s['foundations'].append(self._foundation(x, y, game=self, suit=suit))
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        """
        Deal initial cards for new game.

        .. \_start([]) -> None
        """
        pass

class Cringle(game.Game):
    """
    Finish 2 deck on foundation.

    Layout
      - talon [1 SE, no offset]
      - waste [1 W talon, no offset]
      - foundation [8 horz N, no offset]
      - row [8 horz S foundation, vert offset]

    :CVariables:
      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.WasteTalon
        Deal 1 card on waste on click. Inifinite redeal.

      _waste_ : pysollib.stack.Stack
        .. pysollib.stack.WasteStack
        Init 1 card per. Build none. Play top.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.foundation.AceKingSS_Foundation
        Build in rank by same suit. N up; S down. Play top. Base ace N, king
        S.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.AC_RowStack  (mod=13)
        Init 5 open card per. Build in rank by alt color. W down; E up. Play
        sequence.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.hint.CautiousDefaultHint
    """
    __info__ = dict(id=0x2eb, name='Cringle', game_type=(GT.TWO_DECK |
                    GT.ORIGINAL), decks=2, skill_level=SL.BALANCED,
                    version='1.10fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundations_ = FoundationStack.AceKingSS_Foundation
    _rows_ = (stack.AC_RowStack, Wrap(stack.AC_RowStack, dir=1),)

    def _create_game(self, playcards=14, **kw):
        """
        _create_game([rows=8, playcards=14]) -> Layout

        :returns: current layout with piles and their texts set.

        :todo: needs seperate layout
        """
        l, s = layout.Layout(self), self.s
        self.table_size = (2 * l.XM + l.NSUITS * l.XS,
                     l.YM + 2 * l.YS + l._get_stack_height(playcards))
        i = dx = 0
        for (x_, y_), suit in zip(l.col_block_iter(l.XM, l.YM,
                                   npiles=l.NSUITS, ncols=l.NSUITS), l.SUITS):
            s['foundations'].append(self._foundation_(x_ + dx, y_, game=self,
                                                       suit=suit))
            s['rows'].append(self._rows_[i](x_ + dx, y_ + l.YS, game=self))
            if suit == l.NSUIT[-1]:
                i += 1
                dx += l.XM
        x, y = self.width - l.XS, self.height - l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'n')
        x -= l.XS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 'n')
        l.defaultStackGroups()
        return l

    def _start(self, rows=5, flip=True):
        """
        Deal initial cards for new game.
          - s[rows] 5 ea up.

        .. \_start([rows=5, flip=True]) -> None
        """
        for i in range(rows - 1):
            self.s['talon'].dealRow(flip=flip, frames=0)
        self.s['talon'].dealRow()

PROVIDES = [Bisley, DoubleBisley, Gloria, Realm, Mancunian, HospitalPatience,
            BoardPatience, Cringle]